home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Networking / OTSimpleServerHTTP1.2d2 / OTSimpleServerHTTPTest.c < prev    next >
Encoding:
Text File  |  1997-08-26  |  10.1 KB  |  350 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        OTSimpleServerHTTPTest.c
  3.  
  4.     Contains:    A test program for the simple HTTP server code.
  5.  
  6.     Written by:    Quinn "The Eskimo!"
  7.  
  8.     Copyright:    © 1997 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.     You may incorporate this sample code into your applications without
  13.     restriction, though the sample code has been provided "AS IS" and the
  14.     responsibility for its operation is 100% yours.  However, what you are
  15.     not permitted to do is to redistribute the source as "DSC Sample Code"
  16.     after having made changes. If you're going to re-distribute the source,
  17.     we require that you make it clear in the source that the code was
  18.     descended from Apple Sample Code, but that you've made changes.
  19. */
  20.  
  21. /////////////////////////////////////////////////////////////////////
  22. // The OT debugging macros in <OTDebug.h> require this variable to
  23. // be set.
  24.  
  25. #ifndef qDebug
  26. #define qDebug    1
  27. #endif
  28.  
  29. /////////////////////////////////////////////////////////////////////
  30. // Pick up all the standard OT stuff.
  31.  
  32. #include <OpenTransport.h>
  33.  
  34. /////////////////////////////////////////////////////////////////////
  35. // Pick up all the OT TCP/IP stuff.
  36.  
  37. #include <OpenTptInternet.h>
  38.  
  39. /////////////////////////////////////////////////////////////////////
  40. // Pick up the OTDebugBreak and OTAssert macros.
  41.  
  42. #include <OTDebug.h>
  43.  
  44. /////////////////////////////////////////////////////////////////////
  45. // Some common Mac OS prototypes.
  46.  
  47. #include <Threads.h>
  48.  
  49. /////////////////////////////////////////////////////////////////////
  50. // Pick up SIOUXHandleOneEvent.
  51.  
  52. #include <SIOUX.h>
  53.  
  54. /////////////////////////////////////////////////////////////////////
  55. // Standard C prototypes.
  56.  
  57. #include <stdio.h>
  58. #include <stdlib.h>
  59.  
  60. /////////////////////////////////////////////////////////////////////
  61. // Prototypes for the actual core HTTP server code.
  62.  
  63. #include "OTSimpleServerHTTP.h"
  64.  
  65. /////////////////////////////////////////////////////////////////////
  66. // OTDebugStr is not defined in any OT header files, but it is
  67. // exported by the libraries, so we define the prototype here.
  68.  
  69. extern pascal void OTDebugStr(const char* str);
  70.  
  71. /////////////////////////////////////////////////////////////////////
  72. // The only way to tell whether OT supports IP single link
  73. // multihoming is to check the version number.  The feature was
  74. // added in OT 1.3.  The initialisation code sets 
  75. // gHaveIPSingleLinkMultihoming depending on the version number
  76. // to avoid the rest of the code having to call Gestalt repeatedly.
  77.  
  78. enum
  79. {
  80.     kOTIPSingleLinkMultihomingVersion = 0x01300000
  81. };
  82.  
  83. static Boolean gHaveIPSingleLinkMultihoming;
  84.  
  85. /////////////////////////////////////////////////////////////////////
  86.  
  87. // gLastCallWNE is used to throttle calls to wait next event so that
  88. // we don't call it too often, which can be bad for system performance.
  89.  
  90. static UInt32 gLastCallWNE = 0;
  91.  
  92. // gRunningThreads contains the number of running HTTP listeners.
  93. // We spool an HTTP listener for each IP address on the computer.
  94. // Normally you would only spool one listener for the entire machine
  95. // (listening on kOTAnyInetAddress), but we want to actively distinguish
  96. // between each IP address so that we can server different information
  97. // for each IP address.
  98.  
  99. static UInt32 gRunningThreads = 0;
  100.  
  101. /////////////////////////////////////////////////////////////////////
  102.  
  103. static OSErr FSpGetCatInfo(FSSpecPtr fss, short ioFDirIndex, CInfoPBPtr cpb)
  104.     // A simple wrapper for GetCatInfo.
  105. {
  106.     cpb->hFileInfo.ioVRefNum = fss->vRefNum;
  107.     cpb->hFileInfo.ioDirID = fss->parID;
  108.     cpb->hFileInfo.ioNamePtr = fss->name;
  109.     cpb->hFileInfo.ioFDirIndex = ioFDirIndex;
  110.     return ( PBGetCatInfoSync(cpb) );
  111. }
  112.  
  113. /////////////////////////////////////////////////////////////////////
  114.  
  115. static pascal OSStatus HTTPServerProc(InetHost ipAddr)
  116.     // This routine is the main line of the thread that runs
  117.     // an HTTP server.  ipAddr is the address on which the
  118.     // server is listening.  Specify kOTAnyInetAddress to listen
  119.     // on all active IP addresses simultaneously.
  120.     //
  121.     // The routine uses a directory whose name is the
  122.     // dotted decimal string representation of ipAddr as the
  123.     // root directory of the HTTP server.
  124. {
  125.     OSStatus err;
  126.     Str255 ipAddrString;
  127.     long rootVRefNum;
  128.     long rootDirID;
  129.     FSSpec dirSpec;
  130.     CInfoPBRec cpb;
  131.     
  132.     // Get ipAddr as a dotted decimal Pascal string.
  133.     OTInetHostToString(ipAddr, (char *) ipAddrString);
  134.     C2PStr( (char *) ipAddrString);
  135.     
  136.     // Find the associated dirID, creatintg the directory
  137.     // if necessary.
  138.     
  139.     (void) FSMakeFSSpec(0, 0, ipAddrString, &dirSpec);
  140.     rootVRefNum = dirSpec.vRefNum;
  141.     err = FSpGetCatInfo(&dirSpec, 0, &cpb);
  142.     if (err == noErr && ( (cpb.hFileInfo.ioFlAttrib & (1 << 4)) != 0) ) {
  143.         rootDirID = cpb.hFileInfo.ioDirID;
  144.     } else {
  145.         err = FSpDirCreate(&dirSpec, 0, &rootDirID);
  146.     }
  147.     
  148.     // Start running an HTTP server on the IP address.  This
  149.     // routine won't return under someone sets gQuitNow, which
  150.     // is why we're calling it from a thread.
  151.     
  152.     if (err == noErr) {
  153.         err = RunHTTPServer(ipAddr, rootVRefNum, rootDirID);
  154.     }
  155.     
  156.     gRunningThreads -= 1;
  157.     
  158.     return (err);
  159. }
  160.  
  161. /////////////////////////////////////////////////////////////////////
  162.  
  163. static OSStatus RunOneServer(InetHost ipAddr)
  164.     // Runs a single HTTP server thread, serving the
  165.     // given ipAddr.
  166. {
  167.     OSStatus err;
  168.     ThreadID junkServerThread;
  169.     
  170.     err = NewThread(kCooperativeThread,
  171.                         (ThreadEntryProcPtr) HTTPServerProc, (void *) ipAddr,
  172.                         0, kCreateIfNeeded,
  173.                         nil,
  174.                         &junkServerThread);
  175.     if (err == noErr) {
  176.         gRunningThreads += 1;
  177.     }
  178.     
  179.     return (err);
  180. }
  181.  
  182. /////////////////////////////////////////////////////////////////////
  183.  
  184. static OSStatus RunServersForInterface(InetInterfaceInfo* interfaceInfo, SInt32 interfaceIndex)
  185.     // Run HTTP servers for all of the IP addresses associated
  186.     // with the interface denoted by interfaceInfo and interfaceIndex.
  187.     // This routine first starts a server for the primary address
  188.     // of the interface, then iterates through the secondary addresses on
  189.     // the interface, starting a server thread for each one.
  190. {
  191.     OSStatus err;
  192.     InetHost *secondaryAddressBuffer;
  193.     UInt32   numberOfSecondaryAddresses;
  194.     UInt32   addressIndex;
  195.  
  196.     secondaryAddressBuffer = nil;
  197.     
  198.     // First run the server for the interfaces primary address.
  199.     
  200.     err = RunOneServer(interfaceInfo->fAddress);
  201.     
  202.     // Now start a server for each of the interface's secondary
  203.     // addresses.  This stuff can only be done on systems that
  204.     // support IP single link multihoming.
  205.  
  206.     numberOfSecondaryAddresses = interfaceInfo->fIPSecondaryCount;
  207.     
  208.     if ( err == noErr && gHaveIPSingleLinkMultihoming && numberOfSecondaryAddresses > 0 ) {
  209.  
  210.         // Allocate a buffer for the secondary address info.
  211.         
  212.         secondaryAddressBuffer = (InetHost *) OTAllocMem( numberOfSecondaryAddresses * sizeof(InetHost) );
  213.         if (secondaryAddressBuffer == nil) {
  214.             err = kENOMEMErr;
  215.         }
  216.         
  217.         // Ask OT for the list of secondary addresses on this interface.
  218.         
  219.         if (err == noErr) {
  220.             err = OTInetGetSecondaryAddresses(secondaryAddressBuffer, &numberOfSecondaryAddresses, interfaceIndex);
  221.         }
  222.         
  223.         // Start a server for each secondary address.
  224.         
  225.         addressIndex = 0;
  226.         while (err == noErr && addressIndex < numberOfSecondaryAddresses) {
  227.             err = RunOneServer(secondaryAddressBuffer[addressIndex]);
  228.             if (err == noErr) {
  229.                 addressIndex += 1;
  230.             }
  231.         }
  232.     }
  233.  
  234.     // Clean up.
  235.     
  236.     if (secondaryAddressBuffer != nil) {
  237.         OTFreeMem(secondaryAddressBuffer);
  238.     }
  239.  
  240.     return (err);
  241. }
  242.  
  243. /////////////////////////////////////////////////////////////////////
  244.  
  245. static OSStatus RunAllHTTPServers(void)
  246.     // Run HTTP servers for all of the IP addresses on the machine.
  247.     // This routine iterates through the active Internet interfaces, 
  248.     // starting server threads for each active IP address on each
  249.     // interface.
  250. {
  251.     OSStatus err;
  252.     OSStatus junk;
  253.     EndpointRef dummyEP;
  254.     InetInterfaceInfo info;
  255.     SInt32 interfaceIndex;
  256.     Boolean done;
  257.     TEndpointInfo epInfo;
  258.     
  259.     // Force TCP to load by creating a dummy endpoint.  Otherwise,
  260.     // if we're the first TCP application to run, OTInetGetInterfaceInfo
  261.     // will not return any active interfaces )-:
  262.     
  263.     dummyEP = OTOpenEndpoint(OTCreateConfiguration("tcp"), 0, &epInfo, &err);
  264.     if (err == noErr) {
  265.     
  266.         // Iterate through the interfaces, starting HTTP servers on each.
  267.         
  268.         done = false;
  269.         interfaceIndex = 0; 
  270.         do {
  271.             done = ( OTInetGetInterfaceInfo(&info, interfaceIndex) != noErr );
  272.             if ( ! done ) {
  273.                 err = RunServersForInterface(&info, interfaceIndex);
  274.                 interfaceIndex += 1;
  275.             }
  276.         } while (err == noErr && !done);
  277.     }
  278.     
  279.     if (dummyEP != nil) {
  280.         junk = OTCloseProvider(dummyEP);
  281.         OTAssert("RunAllHTTPServers: Failed closing dummy endpoint", junk == noErr);
  282.     }
  283.     
  284.     return (err);
  285. }
  286.  
  287.  
  288. /////////////////////////////////////////////////////////////////////
  289.  
  290. void main(void)
  291.     // The main line of the application.  This routine performs
  292.     // two functions.  At startup, it initialises the network and
  293.     // starts HTTP servers on all the active IP addresses on the machine.
  294.     // After that it goes into a loop calling WaitNextEvent and
  295.     // waiting for all the listener threads to terminate.  Events
  296.     // are handled by sending them to SIOUX, except when the user
  297.     // presses 'q' the routine sets the gQuitNow boolean to force
  298.     // all the server threads to terminate.
  299. {
  300.     OSStatus err;
  301.     OSStatus junk;
  302.     EventRecord event;
  303.     NumVersionVariant otVersion;
  304.     
  305.     printf("OTSimpleServerHTTP!\n");
  306.     printf("-- World's dumbest HTTP server.\n");
  307.     printf("-- Press 'q' to stop the server.\n");
  308.     printf("\n");
  309.  
  310.     gQuitNow = false;
  311.     
  312.     err = InitOpenTransport();
  313.     if (err == noErr) {
  314.  
  315.         gHaveIPSingleLinkMultihoming = ( Gestalt(gestaltOpenTptVersions, (long *) &otVersion) == noErr
  316.                                             && (otVersion.whole >= kOTIPSingleLinkMultihomingVersion ) );
  317.         gRunningThreads = 0;
  318.         
  319.         err = RunAllHTTPServers();
  320.         
  321.         while ( gRunningThreads != 0 ) {
  322.             if ( TickCount() > (gLastCallWNE + 3) ) {
  323.                 (void) WaitNextEvent(everyEvent, &event, 0, nil);
  324.                 if (event.what == keyDown) {
  325.                     if ( (event.message & charCodeMask) == 'q' ) {
  326.                         gQuitNow = true;
  327.                         printf("Setting gQuitNow.\n");
  328.                         fflush(stdout);
  329.                     }
  330.                 }
  331.                 (void) SIOUXHandleOneEvent(&event);
  332.                 gLastCallWNE = TickCount();
  333.             }
  334.             junk = YieldToAnyThread();
  335.             OTAssert("main: YieldToAnyThread failed", junk == noErr);
  336.         }
  337.         
  338.         CloseOpenTransport();
  339.     }
  340.     
  341.     if (err == noErr) {
  342.         printf("Success.\n");
  343.     } else {
  344.         printf("Failed with error %d.\n", err);
  345.     }    
  346.     printf("Done.  Press command-Q to Quit.\n");
  347. }
  348.  
  349.  
  350.